bpo-36389: Add gc.enable_object_debugger()#12480
bpo-36389: Add gc.enable_object_debugger()#12480vstinner wants to merge 2 commits intopython:masterfrom vstinner:gc_object_debugger
Conversation
|
Thanks @csabella, I applied your suggestions. I rebased my PR to edit fix the commit message. |
|
Second version of my change. The object debugger now longer calls _PyXXX_CheckConsistency() functions, but rather reuse tp_traverse mecanism and only implements the most consistency checks: The documentation now explains which checks are implemented and says explicitly that the debugger is written to find bugs in C extensions. It now also mentions that the debugger rely on the debug hooks on memory allocators and shortly explains how to enable them. |
|
Small update: rebase on top of new better _PyObject_IsFreed() implementation, and fix where gc_check_object_debugger() is called in Modules/gcmodule.c. |
New "object debugger" which checks frequently if all Python objects tracked by the garbage collector are consistent: gc.enable_object_debugger() and gc.disable_object_debugger(). * Py_FatalError() and _PyObject_AssertFailed() now disable the GC object debugger to prevent reentrant calls. * Fix _PyObject_Dump() for ob_type=NULL
Disable debugger in PyObject_GC_Del() and _PyObject_GC_Resize(): the debugger is just too slow.
|
New rebase. I added multiple thresholds to get to reduce the performance overhead. I compared the number of _PyGC_ObjectDebuggerGeneration(0) calls using gc.enable_object_debugger(5, 10, 10) vs collect(0) calls using gc.set_threshold(5, 10, 10): _PyGC_ObjectDebuggerGeneration(0) is called 10x more times. My raw stats: Ah, and _PyGC_ObjectDebuggerGeneration(2) is called 52x more than than collect(2)... |
| { | ||
| struct _gc_object_debugger *debugger = &_PyRuntime.gc.object_debugger; | ||
| debugger->enabled = 0; | ||
| for (int i=0; i < NUM_GENERATIONS; i++) { |
There was a problem hiding this comment.
| for (int i=0; i < NUM_GENERATIONS; i++) { | |
| for (int i = 0; i < NUM_GENERATIONS; i++) { |
| GC_OBJECT_ASSERT(op, type != NULL); | ||
| GC_OBJECT_ASSERT(op, !_PyObject_IsFreed((PyObject *)type)); | ||
|
|
||
| #undef ASSERT |
|
|
||
|
|
||
| /*[clinic input] | ||
| gc.enable_object_debugger as gc_py_enable_object_debugger -> NoneType |
There was a problem hiding this comment.
I would not use -> NoneType. It does not add clarity. Actually return Py_None without increfing looks suspicious. It is clearer to use Py_RETURN_NONE.
|
|
||
| This debugger aims to debug bugs in C extensions. | ||
|
|
||
| .. function:: enable_object_debugger(threshold) |
There was a problem hiding this comment.
The signature does not match implementation.
| int threshold; /* collection threshold */ | ||
| int count; /* count of allocations or collections of younger | ||
| generations */ | ||
| } generations[NUM_GENERATIONS]; |
There was a problem hiding this comment.
Sorry, I have not found where multiple generations are used?
|
Abandoned for reasons explained at: https://bugs.python.org/issue36389#msg347497 |
New "object debugger" which checks frequently if all Python object tracked
by the garbage collector are consistent: gc.enable_object_debugger()
and gc.disable_object_debugger().
now exposed in the internal API. _PyDict_CheckConsistency()
parameter type becomes PyObject*.
https://bugs.python.org/issue36389